:- dynamic indent/1,closure_arg/2.

syso(String):-
	format(user_output, String, _Args1),
	format(user_output, '\n', _Args2).
syso(String, Args):-
	format(user_output, String, Args),
	format(user_output, '\n', _Args).

semicolon_nl:-
	format(';'),nl.

:- retractall(indent(_)).
indent(0).
indent:-
	indent(CurrVal),
	NextVal is CurrVal+1,
	% syso('indentation lvl is ~w',[NextVal]),
	retract(indent(_)),
	assert(indent(NextVal)).
unindent:-
	indent(CurrVal),
	NextVal is CurrVal-1,
	% syso('indentation lvl is ~w',[NextVal]),
	(NextVal<0 ->
	 (syso('negative indent!'),throw(negative_indent));
	 true),
	retract(indent(_)),
	assert(indent(NextVal)).

indent_code(0):-!.
indent_code(Int):-
	format('\t'),
	Next is Int-1,
	indent_code(Next).
format_code(Code):-
	\+ atom(Code),!,
	% syso('calling Code ~w',[Code]),
	call(Code).
format_code(String):-
	indent(Lvl),
	indent_code(Lvl),
	% syso('formatting ~w',[String]),
	format(String, _Args1).
format_code(String,Args):-
	indent(Lvl),
	indent_code(Lvl),
	% syso('formatting ~w',[String]),
	format(String, Args).

comment(String):-
	format_code('//~w',[String]),nl.
comment(String,Comment):-
	atomic_list_concat(['//',String],Comment).

comment_sorround(Code,Comment):-
	atom_concat('START ',Comment,StartComment),
	atomic_list_concat(['END ',Comment,'\n'],EndComment),
	comment(StartComment),
	% call(Code),
	format_code(Code),
	comment(EndComment).

quote(String,Quoted):-
	atomic_list_concat(['"',
			   String,
			   '"'],Quoted).

include_file(Filename):-
        atomic_list_concat(['include "',
                            Filename,
                            '.galaxy"'],Inc),
        format(Inc),nl.

neg(Cond,Neg):-
	atomic_list_concat(['!(',Cond,')'],Neg).

or_cond(Arg1,Arg2,Cond):-
	atomic_list_concat([Arg1,' || ',Arg2],Cond).

eqeq_cond(Var1,Var2,Cond):-
	atomic_list_concat([Var1, ' == ', Var2],Cond).

neq_cond(Var1,Var2,Cond):-
	atomic_list_concat([Var1, ' != ', Var2],Cond).

gte_cond(Var1,Var2,Cond):-
	atomic_list_concat([Var1, ' <= ', Var2],Cond).

var_setter(VarName,Val):-
	format_code('~w = ~w',[VarName,Val]),semicolon_nl.

var_setter(VarName,Val,Setter):-
	atomic_list_concat([VarName,' = ',Val,';\n'],Setter).

trigger_var_setter(VarName,FuncName,Setter):-
	quote(FuncName,Quoted),
	func_call('TriggerCreate',[Quoted],Call),
        var_setter(VarName,Call,Setter).
trigger_var_setter(VarName,FuncName):-
        trigger_var_setter(VarName,FuncName,Setter),
        format_code(Setter).
        
var_declarator(Type,VarName):-
	format_code('~w ~w',[Type,VarName]),semicolon_nl.

var_declarator(Type,VarName,Val):-
	var_setter(VarName,Val,Setter),
	atomic_list_concat([Type,' ',Setter],Declarator),
	format_code(Declarator).

var_declarator(Type,VarName,Val,Declarator):-
	var_setter(VarName,Val,Setter),
	atomic_list_concat([Type,' ',Setter],Declarator).

array_len_var(ArrName,VarName):-
	atomic_list_concat([ArrName,'_LEN'],VarName).

array_len_var(ArrName):-
	format_code('~w_LEN',[ArrName]).

array_len_var_declarator(ArrName,Val):-
	array_len_var(ArrName,ArrVar),
	atomic_list_concat(['int ',ArrVar,' = ',Val],Declarator),
	format_code(Declarator),semicolon_nl.
	
array_var_declarator(Var,Type,Len):-
	array_len_var_declarator(Var,Len),
	atomic_list_concat([Type,'[',Len,'] ',Var],Declarator),
	format_code(Declarator),semicolon_nl.

array_var_accessor(Var,I,Accessor):-
	atomic_list_concat([Var,'[',I,']'],Accessor).

array_var_setter(Var,I,Val):-
	array_var_setter(Var,I,Val,Set),
        format_code(Set).
array_var_setter(Var,I,Val,Set):-
	atomic_list_concat([Var,'[',I,'] = ',Val,';\n'],Set).

'+='(Num,Addend,Setter):-
	atomic_list_concat([Num,' = ',Num,' + ',Addend,';\n'],Setter).

:- op(400,xfx,nextInt).

nextInt(Num):-
        nextInt(Num,Setter),
        format_code(Setter).
nextInt(Num,Setter):-
	+=(Num,1,Setter).

return_statement(RetVal):-
	atomic_list_concat(['return ',RetVal,';\n'],RetStat),
	format_code(RetStat).

ret:-
	format_code('return;\n').

ret_true:-
	format_code('return true;\n').

ret_false:-
	format_code('return false;\n').

return_statement(RetVal,RetStat):-
	atomic_list_concat(['return ',RetVal,';'],RetStat).

if_statement(Cond,Body):-
	(atom(Cond) -> 
	 format_code('if(~w){',[Cond]),nl;
	 format_code('if('),call(Cond),format('){'),nl),
	indent,
	format_code(Body),
	unindent,
	format_code('}'),nl.
if_statement(Cond,Body,Else):-
        if_statement(Cond,Body),
        format_code('else{'),nl,
        indent,
        format_code(Else),
        unindent,
        format_code('}'),nl.

break_while:-
        format_code('break;'),nl.

continue_while:-
        format_code('continue;'),nl.

while_loop(Start,Cond,Code,Step):-
	format_code(Start),
	while_loop(Cond,Code,Step).

while_loop(Cond,Code,Step):-
	format_code('while(~w){',[Cond]),nl,
	indent,
	format_code(Code),
	format_code(Step),
	unindent,
	format_code('}'),nl.

loop_over_array(ArrVar,Code,Ind):-
        loop_over_array(ArrVar,Code,Ind,IndDec,Loop),
        format_code(IndDec),
        format_code(Loop).
loop_over_array(ArrVar,Code,Ind,IndDec,Loop):-
	array_len_var(ArrVar,VarName),
	loop_index_idgen(ID),
	atom_concat('loopIndex',ID,Ind),
	atomic_list_concat([Ind,'<',VarName],Cond),
        IndDec = var_declarator(int,Ind),
        Start = var_setter(Ind,0),
	Ind nextInt Step,
	Loop = while_loop(Start,Cond,Code,Step).

func_sig_arglist([],'()'):-!.
func_sig_arglist(Args,Arglist):-
	func_sig_arglist_helper(Args,FormattedArgs),
	atomic_list_concat(['(',FormattedArgs,')'],Arglist).
func_sig_arglist_helper([[ArgType,ArgName]],Formatted):-
	atomic_list_concat([ArgType,' ',ArgName],Formatted),!.
func_sig_arglist_helper([[ArgType,ArgName]|Rest],FormattedArgs):-
	func_sig_arglist_helper(Rest,Formatted),
	atomic_list_concat([ArgType,' ',ArgName,', ',Formatted],FormattedArgs).

func_declarator(RetType,FuncName,Args,Body):-
	% syso('declaring func ~w with',[FuncName]),
	func_sig_arglist(Args,FormattedArgs),
	% syso('formatted args ',[FormattedArgs]),
	format_code('~w ~w~w{',[RetType,FuncName,FormattedArgs]),nl,
	indent,
	format_code(Body),
	unindent,
	format_code('}'),nl,nl.

func_call_arg_val_list([],'()'):-!.
func_call_arg_val_list(Args,Ans):-
	func_call_arg_val_list_helper(Args,FormattedArgs),
	atomic_list_concat(['(',FormattedArgs,')'], Ans).
func_call_arg_val_list_helper([Arg],Arg):-!.
func_call_arg_val_list_helper([Arg|Rest],Formatted):-
	func_call_arg_val_list_helper(Rest,FormattedRest),
	atomic_list_concat([Arg,',',FormattedRest],Formatted).

func_call(FuncName,Args):-
        func_call(FuncName,Args,Call),
        format_code(Call),semicolon_nl.
func_call(FuncName,Args,Call):-
	func_call_arg_val_list(Args,FormattedArgVals),
	atomic_list_concat([FuncName,FormattedArgVals],Call).

trigger_declarator(TriggerFuncName,TriggerVarName,Body):-
        trigger_declarator(TriggerFuncName,(Body,ret_true)),
	quote(TriggerFuncName,Quoted),
	func_call('TriggerCreate',[Quoted],Call),
	var_declarator(trigger,TriggerVarName,Call),nl.
trigger_declarator(TriggerFuncName,Body):-
	func_declarator(bool,
			TriggerFuncName,
			[[bool,testConds],
			 [bool,doActions]],
			(Body,ret_true)).

spec_member_name(Spec,MemName,Name):-
	upcase_atom(MemName, UpMem),
	atomic_list_concat(['SPEC_',Spec,'_',UpMem],Name).

spec_bevent_var_name(Spec,E,VarName):-
	spec_member_name(Spec,bevent_,EventPrefix),
	atom_concat(EventPrefix,E,VarName).

spec_bthread_member_name(Spec,T,MemName,Name):-
	upcase_atom(MemName,UpMemName),
	spec_member_name(Spec,bthread_,SpecThreadPrefix),
	atomic_list_concat([SpecThreadPrefix,T,'_'],Prefix),
	atom_concat(Prefix,UpMemName,Name).

spec_bthread_state_member_name(Spec,T,S,MemName,Name):-
	upcase_atom(MemName,UpMemName),
	spec_bthread_member_name(Spec,T,state_,SpecThreadStatePrefix),
	atomic_list_concat([SpecThreadStatePrefix,S,'_',UpMemName],Name).
	
closure_name(Spec,Name,ClosureName):-
	spec_member_name(Spec,'',Prefix),
	atomic_list_concat([Prefix,Name,'_CLOSURE'],ClosureName).
	
closure_name_formatter(Spec,Name):-
	closure_name(Spec,Name,ClosureName),
	format(ClosureName).

closure_out_name(ClosureName,OutName,ResName):-
	atomic_list_concat([ClosureName,OutName],ResName).
	
closure_out_name_formatter(Spec,Name):-
	closure_out_name(Spec,Name,ResName),
	format(ResName).

% dummy rule to avoid exception on closures with no args
closure_arg(dummy,dummyArg).
closure_declarator(Spec,Name,Code,Args):-
	closure_declarator(Spec,Name,Code,Args,_).
closure_declarator(Spec,Name,Code,Args,Out):-
	closure_name(Spec,Name,ClosureName),
	syso('declaring closure ~w',[ClosureName]),
	forall(member([Type,ArgName],Args),
	       (var_declarator(Type,ArgName),
		assertz(closure_arg(ClosureName,ArgName)),
		syso('asserted closure arg ~w.~w',[ClosureName,ArgName])
	       )),
	((is_list(Out),Out = [OutType,OutName]) ->
	 closure_out_name(ClosureName,OutName,OutVarName),
	 var_declarator(OutType,OutVarName);
	 true),
	spec_member_name(Spec,Name,ClosureTriggerVar),
	trigger_declarator(ClosureName,ClosureTriggerVar,Code).

set_params_for_closure_call(ClosureName,ArgVals):-
	findall(ArgName,
		closure_arg(ClosureName,ArgName),
		ArgNames),
	% syso('argnames are ~w',[ArgNames]),
	% syso('argvals are ~w',[ArgVals]),
	set_params_for_closure_call_helper(ArgNames,ArgVals).

set_params_for_closure_call_helper([],[]).
set_params_for_closure_call_helper([Name|Names],[Val|Vals]):-
	var_setter(Name,Val),
	set_params_for_closure_call_helper(Names,Vals).

closure_call_asynch(Name,ArgVals):-
	closure_call(Name,ArgVals,force,asynch).
closure_call_synch(Name,ArgVals):-
	closure_call(Name,ArgVals,force,join).
closure_call(Name,ArgVals,Force,Asynch):-
	% syso('formatting call to closure ~w',[Name]),
	% syso('setting up params for closure call'),
	set_params_for_closure_call(Name,ArgVals),
	func_call('TriggerExecute',[Name,
				    Force,
				    Asynch],Call),
	format_code(Call),semicolon_nl.

% system globals formatting
triggered_event_var_name(Spec,Name):-
	spec_member_name(Spec,triggered_event,Name).

format_triggered_event_var(Spec):-
	spec_member_formatter(Spec,triggered_event).

gen_list(Spec,Name,Type,Len):-
	spec_member_name(Spec,Name,ArrName),
	array_var_declarator(ArrName,
			     Type,
			     Len).

var_setter_func(Spec,Name,Type):-
        atom_concat(set_,Name,Setter),
        atom_concat(new,Name,NewVal),
        spec_member_name(Spec,Setter,SetterFuncName),
        spec_member_name(Spec,Name,SpecVar),
        func_declarator(void,
                        SetterFuncName,
                        [[Type,NewVal]],
                        var_setter(SpecVar,NewVal)),
        syso('generated setter for ~w',[Name]).

var_getter_func(Spec,Name,Type):-
        atom_concat(get_,Name,Getter),
        spec_member_name(Spec,Getter,GetterFuncName),
        spec_member_name(Spec,Name,SpecVar),
        func_declarator(Type,
                        GetterFuncName,
                        [],
                        return_statement(SpecVar)),
        syso('generated getter for ~w',[Name]).

var_setter_getter(Spec,Name,Type):-
        var_setter_func(Spec,Name,Type),
        var_getter_func(Spec,Name,Type).

gen_var(Spec,Name,Type):-
	spec_member_name(Spec,Name,VarName),
	var_declarator(Type,VarName).

gen_var(Spec,Name,Type,InitVal):-
	spec_member_name(Spec,Name,VarName),
	var_declarator(Type,VarName,InitVal).
%===============================================================================

:- syso('Galaxy formatter loaded.').